/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-04-10     ZYH          first version
 * 2019-10-27     flybreak     Compatible with the HS
 */

#include <rtthread.h>
#include "board.h"

#define BSP_USING_USBDEVICE
#ifdef BSP_USING_USBDEVICE

#include <rtdevice.h>
#include <string.h>
#include <drv_config.h>
#include "hal_data.h"
#include "r_usb_typedef.h"
#define EP_ADDR_MSK                            0xFU

/* Global variables */
extern uint8_t g_apl_device[];
extern uint8_t g_apl_configuration[];
extern uint8_t g_apl_hs_configuration[];
extern uint8_t g_apl_qualifier_descriptor[];
extern uint8_t *g_apl_string_table[];
extern int g_curr_led_freq;

#define NUM_STRING_DESCRIPTOR               (7U)
#define LINE_CODING_LENGTH          (0x07U)
#define READ_BUF_SIZE               (8U)
#define KIT_INFO              ('1')
#define NEXT_STEPS            ('2')
#define CARRIAGE_RETURN       ('\r')

const usb_descriptor_t g_usb_descriptor =
{
 g_apl_device,                   /* Pointer to the device descriptor */
 g_apl_configuration,            /* Pointer to the configuration descriptor for Full-speed */
 g_apl_hs_configuration,         /* Pointer to the configuration descriptor for Hi-speed */
 g_apl_qualifier_descriptor,     /* Pointer to the qualifier descriptor */
 g_apl_string_table,             /* Pointer to the string descriptor table */
 NUM_STRING_DESCRIPTOR
};

/* Global variables for the USB */
usb_status_t usb_event;
usb_setup_t usb_setup;
uint8_t g_usb_module_number = 0x00;
usb_class_t g_usb_class_type = 0x00;

static rt_uint8_t usb_event_stack[RT_USBD_THREAD_STACK_SZ];
static struct rt_thread usb_event_thread;
static rt_err_t _ep_disable(uep_t ep);
struct udcd _stm_udc;
static struct ep_id _ep_pool[] =
{
    {0x0,  USB_EP_ATTR_CONTROL,     USB_DIR_INOUT,  64, ID_ASSIGNED  },
    {0x1,  USB_EP_ATTR_BULK,        USB_DIR_IN,     64, ID_UNASSIGNED},
    {0x2,  USB_EP_ATTR_BULK,        USB_DIR_OUT,    64, ID_UNASSIGNED},
    {0x6,  USB_EP_ATTR_INT,         USB_DIR_IN,     64, ID_UNASSIGNED},
};

static rt_err_t _ep_set_stall(rt_uint8_t address)
{
    usb_utr_t           tran_data_peri;

    tran_data_peri.ip = g_basic0_ctrl.module_number;

    extern uint16_t g_usb_usbmode[];
    if (USB_MODE_PERI == g_usb_usbmode[g_basic0_ctrl.module_number])
    {
        if ((address & EP_ADDR_MSK) == 0U)
        {
            usb_pstd_set_stall_pipe0(&tran_data_peri);
        }
        else
        {
            usb_pstd_set_stall(address & EP_ADDR_MSK, &tran_data_peri);
        }
     }
    return RT_EOK;
}

static rt_err_t _ep_clear_stall(rt_uint8_t address)
{
//    HAL_PCD_EP_ClrStall(&_stm_pcd, address);
    return RT_EOK;
}

static rt_err_t _set_address(rt_uint8_t address)
{
//    HAL_PCD_SetAddress(&_stm_pcd, address);
    return RT_EOK;
}

static rt_err_t _set_config(rt_uint8_t address)
{
    return RT_EOK;
}
#define USB_CURPIPE               (0x000FU) /* b2-0: PIPE select */
static rt_err_t _ep_enable(uep_t ep)
{
    usb_utr_t utr;
    uint8_t pipe_no;
    RT_ASSERT(ep != RT_NULL);
    RT_ASSERT(ep->ep_desc != RT_NULL);
    utr.ip = g_basic0_ctrl.module_number;
    pipe_no = ep->ep_desc->bEndpointAddress & EP_ADDR_MSK;

    rt_kprintf("_ep_enable: %d \n",pipe_no);
    /* Initialization of registers associated with specified pipe. */
    usb_cstd_pipe_init(&utr, pipe_no);

    return RT_EOK;
}

static rt_err_t _ep_disable(uep_t ep)
{
    usb_utr_t utr;
    uint8_t pipe_no;
    RT_ASSERT(ep != RT_NULL);
    RT_ASSERT(ep->ep_desc != RT_NULL);
    utr.ip = g_basic0_ctrl.module_number;
    pipe_no = ep->ep_desc->bEndpointAddress & EP_ADDR_MSK;

    /* Clear specified pipe configuration register. */
    usb_cstd_clr_pipe_cnfg(&utr, pipe_no);
    return RT_EOK;
}

static rt_size_t _ep_read(rt_uint8_t address, void *buffer)
{
    rt_size_t size = 0;
    RT_ASSERT(buffer != RT_NULL);
    return size;
}

static rt_size_t _ep_read_prepare(rt_uint8_t address, void *buffer, rt_size_t size)
{
    uint32_t ret ;
    if ((address & EP_ADDR_MSK) == 0U)
    {
        R_USB_PeriControlDataGet (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size);
    }
    else
    {
        ret = R_USB_Read (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size, (uint8_t) g_usb_class_type);
        if (FSP_SUCCESS != ret)
        {
            rt_kprintf("R_USB_Read error : %d",R_USB_Read);
        }
    }

    return size;
}

static rt_size_t _ep_write(rt_uint8_t address, void *buffer, rt_size_t size)
{
    if ((address & EP_ADDR_MSK) == 0U)
    {
        R_USB_PeriControlDataSet (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size);
    }
    else
    {
        R_USB_Write (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size, (uint8_t) g_usb_class_type);
    }

    return size;
}

static rt_err_t _ep0_send_status(void)
{
    usb_utr_t           tran_data_peri;

    tran_data_peri.ip = g_basic0_ctrl.module_number;

    extern uint16_t g_usb_usbmode[];
    if (USB_MODE_PERI == g_usb_usbmode[g_basic0_ctrl.module_number])
    {
        usb_cstd_set_buf(&tran_data_peri, (uint16_t) USB_PIPE0);

        usb_pstd_ctrl_end((uint16_t) USB_CTRL_END, &tran_data_peri); /* Control transfer stop(end) */
     }
    return RT_EOK;
}

static rt_err_t _suspend(void)
{
    return RT_EOK;
}

static rt_err_t _wakeup(void)
{
    return RT_EOK;
}

static void usbd_event_entry(void* parameter)
{
   static usb_pcdc_linecoding_t g_line_coding;

   while(1)
   {
       rt_thread_mdelay(2);
       /* Obtain USB related events */
       R_USB_EventGet (&g_basic0_ctrl, &usb_event);

   }
}
static rt_err_t _init(rt_device_t device)
{
    fsp_err_t err                           = FSP_SUCCESS;
    /* Open USB instance */
    err = R_USB_Open (&g_basic0_ctrl, &g_basic0_cfg);

    /* Get USB class type */
    err = R_USB_ClassTypeGet (&g_basic0_ctrl, &g_usb_class_type);

    /* Get module number */
    err = R_USB_ModuleNumberGet(&g_basic0_ctrl, &g_usb_module_number);

    /* init usb device thread */
    rt_thread_init(&usb_event_thread,
                   "usbe",
                   usbd_event_entry, RT_NULL,
                   usb_event_stack, RT_USBD_THREAD_STACK_SZ,
                   RT_USBD_THREAD_PRIO-1, 20);
    /* rt_thread_init should always be OK, so start the thread without further
     * checking. */
    rt_thread_startup(&usb_event_thread);
    return RT_EOK;
}

const static struct udcd_ops _udc_ops =
{
    _set_address,
    _set_config,
    _ep_set_stall,
    _ep_clear_stall,
    _ep_enable,
    _ep_disable,
    _ep_read_prepare,
    _ep_read,
    _ep_write,
    _ep0_send_status,
    _suspend,
    _wakeup,
};

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops _ops =
{
    _init,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
};
#endif



int stm_usbd_register(void)
{
    rt_memset((void *)&_stm_udc, 0, sizeof(struct udcd));
    _stm_udc.parent.type = RT_Device_Class_USBDevice;
#ifdef RT_USING_DEVICE_OPS
    _stm_udc.parent.ops = &_ops;
#else
    _stm_udc.parent.init = _init;
#endif
    _stm_udc.parent.user_data = RT_NULL;
    _stm_udc.ops = &_udc_ops;
    /* Register endpoint infomation */
    _stm_udc.ep_pool = _ep_pool;
    _stm_udc.ep0.id = &_ep_pool[0];
#ifdef BSP_USBD_SPEED_HS
    _stm_udc.device_is_hs = RT_TRUE;
#endif
    rt_device_register((rt_device_t)&_stm_udc, "usbd", 0);
    rt_usb_device_init();
    return RT_EOK;
}
INIT_DEVICE_EXPORT(stm_usbd_register);
#endif
